package top.monddia.netty.client.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import top.monddia.netty.client.client.initializer.ClientChannelInitializer;
import top.monddia.netty.client.config.NettyConfig;

import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * Netty客户端
 *
 * @author ZYP
 */
@Component
public class NettyClient implements CommandLineRunner {

    /**
     * 客户端版本
     */
    public final byte VERSION = 1;

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private NettyConfig nettyConfig;

    @Resource
    private ClientChannelInitializer clientChannelInitializer;

    @Override
    public void run(String... args) {
        // 创建配置参数
        Bootstrap bootstrap = new Bootstrap();
        // 创建workGroup用于接收消息数据
        EventLoopGroup workGroup = new NioEventLoopGroup();
        // 建立连接
        connect(bootstrap, workGroup);
    }

    /**
     * 建立socket连接
     *
     * @param bootstrap
     * @param workGroup
     */
    public void connect(Bootstrap bootstrap, EventLoopGroup workGroup) {
        try {
            // 传入当前客户端
            this.clientChannelInitializer.setNettyClient(NettyClient.this);
            // 配置参数
            bootstrap
                    // 绑定处理Group
                    .group(workGroup)
                    // Socket通道类
                    .channel(NioSocketChannel.class)
                    // 保持连接
                    .option(ChannelOption.SO_KEEPALIVE, this.nettyConfig.isSoKeepalive())
                    // 有数据立即发送
                    .option(ChannelOption.TCP_NODELAY, this.nettyConfig.isTcpNoDelay())
                    // 处理新连接
                    .handler(this.clientChannelInitializer);
            // 请求的地址
            InetSocketAddress inetSocketAddress = new InetSocketAddress(this.nettyConfig.getHost(), this.nettyConfig.getPort());
            // 建立连接并且添加监听
            ChannelFuture channelFuture = bootstrap.connect(inetSocketAddress).addListener((ChannelFuture listener) -> {
                // 当前执行器
                final EventLoop eventLoop = listener.channel().eventLoop();
                // 判断是否连接成功
                if (listener.isSuccess()) {
                    logger.info("建立连接成功");
                    // TODO 业务逻辑 listener.channel()
                } else {
                    logger.error("建立连接失败，将在5秒后进行重连");
                    listener.channel().eventLoop().schedule(() -> connect(new Bootstrap(), eventLoop), 5, TimeUnit.SECONDS);
                }
            });
            // 监听到结束信号后关闭
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error("Netty客户端启动失败", e);
        } finally {
            // 如果加了这行就没有重连机制
            // workGroup.shutdownGracefully();
        }
    }
}
